import {CreateElement,PropOptions,PropType,VNode,VNodeChildren} from 'vue';
import {AppendTo,FieldGetter,Formatter,PlainObject,SizeObj} from '../../../../types';
import 'tippy.js/animations/perspective.css';
import Tippy from '../tippy';
import Icon from '../icon';
import TreeMixin,{ComputedObj,RawObj} from '../../js/pc/mixin/treeMixin';
import {QSpinner,QVirtualScroll} from 'quasar';
import Checkbox from '../checkbox';
import mixins from 'vue-typed-mixins';
import {NO_DATA} from '../../js/pc/message';
/**
 * 选择器形状
 */
export type ShapeType='square'|'round'|'noBorder';
/**
 * 节点信息
 * @property {ComputedObj} meta		计算后的节点信息
 */
export interface NodeScope{
	meta:ComputedObj,
}
/**
 * 树选择器
 * @author 唐国雄
 * @date 2021/8/30
 * 属性----------------------
 * v-model						当前值, any
 * :shape						外形, ShapeType
 * :option-value				值属性, string
 * :option-label				显示属性, string | FieldGetter<NodeScope>
 * :children-key				子节点字段, string
 * :data						数据列表, Array<RawObj>
 * :obj-key						主键, string
 * :no-data-label				没有数据时提示, string
 * :format						格式化, Formatter<any>
 * :emit-option					发送整个option对象, boolean
 * :disable						禁用, boolean
 * :append-to					下拉框绑定位置, 默认document.body, AppendTo
 * :inner-style					内部样式, PlainObject
 * :virtual-scroll				虚拟滚动, boolean
 * :virtual-scroll-slice-size	最小渲染项(virtual-scroll必须为true), number | string
 * :virtual-scroll-item-size	每项默认高度(virtual-scroll必须为true), number | string
 * 事件----------------------
 * @input						值改变事件
 */
export default mixins(TreeMixin).extend({
	name:'TreeSelect',
	props:{
		//当前值
		value:{
			required:true,
		} as PropOptions<any>,
		//外形
		shape:{
			type:String,
			default:'square',
			validator(v){
				return [
					'square',
					'round',
					'noBorder',
				].indexOf(v)!==-1;
			}
		} as PropOptions<ShapeType>,
		//值属性
		optionValue:{
			type:String,
			default:'value'
		},
		//显示属性
		optionLabel:{
			type:[String,Function],
			default:'label'
		} as PropOptions<string|FieldGetter<NodeScope>>,
		//子节点字段
		childrenKey:{
			type:String,
			default:'children',
		},
		//没有数据时提示
		noDataLabel:{
			type:String,
			default:NO_DATA,
		},
		//格式化
		format:Function as PropType<Formatter<any>>,
		//发送整个option对象
		emitOption:Boolean,
		//禁用
		disable:Boolean,
		//下拉框绑定位置, 默认document.body
		appendTo:{
			type:[String,Element,Function],
			validator(v){
				if(typeof v==='string'){
					return v==='parent';
				}
				return true;
			}
		} as PropOptions<AppendTo>,
		//内部样式
		innerStyle:Object as PropOptions<PlainObject>,
		//虚拟滚动
		virtualScroll:Boolean,
		//最小渲染项(virtual-scroll必须为true)
		virtualScrollSliceSize:[Number,String],
		//每项默认高度(virtual-scroll必须为true)
		virtualScrollItemSize:[Number,String],
	},
	data(){
		return {
			//组件宽度
			clientWidth:0,
			//下拉弹窗实际宽度
			realWidth:0,
			//是否聚焦选择器
			focus:false,
			scrollTarget:null,
		};
	},
	computed:{
		classes():string{
			return 'lms-TreeSelect'+
				` lms--${this.shape}`+
				(this.focus?' lms--focus':'')+
				(this.innerSelected==null?' lms--none':'')+
				(this.disable?' lms--disable':'');
		},
		showValue():string{
			if(this.innerSelected){
				return this.getCellValue({meta:{rawObj:this.innerSelected} as ComputedObj});
			}
			return '请选择';
		},
		//传给Tippy组件的属性
		tippyOptions():PlainObject{
			return {
				advanced:true,
				head:false,
				disable:this.disable,
				appendTo:this.appendTo,
				width:0,
				minWidth:this.clientWidth,
				'tippy-options':{
					arrow:false,
					placement:'bottom-start',
					duration:[300,300],
					animation:'perspective',
					offset:[0,5],
					theme:'lexmis-widget',
				}
			};
		},
	},
	watch:{
		value:{
			handler(){
				const {emitOption,optionValue,value,innerSelected}=this;
				if(
					innerSelected&&
					innerSelected[optionValue]===(
						emitOption
							?value!=null&&value[optionValue]
							:value
					)
				){
					return;
				}
				// @ts-ignore
				const allComputedObjs=this.treeInfo.allComputedObjs;
				for(let i=0;i<allComputedObjs.length;++i){
					const row=allComputedObjs[i].rawObj;
					if(
						emitOption&&typeof value!=='string'
							?value!=null&&row[optionValue]===value[optionValue]
							:row[optionValue]===value
					){
						this.expandedToNode(allComputedObjs[i].key);
						if(this.selected!==undefined){
							this.$emit('update:selected',row);
						}else{
							this.innerSelected=row;
						}
						return;
					}
				}
				if(this.selected!==undefined){
					this.$emit('update:selected',null);
				}else{
					this.innerSelected=null;
				}
			},
			immediate:true,
		}
	},
	methods:{
		$_lms_show(){
			this.focus=true;
			this.clientWidth=this.$el.clientWidth;
			this.$nextTick(function(){
				// @ts-ignore
				this.scrollTarget=this.$refs.Tippy.$refs.scroll.getVScrollTarget();
			});
		},
		$_lms_hide(){
			this.focus=false;
		},
		$_lms_renderNodes(h:CreateElement,computedObjs:Array<ComputedObj>){
			if(this.virtualScroll){
				return h(QVirtualScroll,{
					staticClass:'lms-TreeSelect_options',
					staticStyle:{
						minWidth:this.realWidth+'px',
					},
					ref:'virtualScroll',
					props:{
						scrollTarget:this.scrollTarget,
						virtualScrollSliceSize:this.virtualScrollSliceSize,
						virtualScrollItemSize:this.virtualScrollItemSize,
						items:computedObjs,
					},
					scopedSlots:{
						default:({item}) => this.$_lms_renderNode(h,item)
					}
				});
			}else{
				return h('div',{
					staticClass:'lms-TreeSelect_options',
					staticStyle:Object.assign({
						minWidth:this.realWidth+'px',
					},this.innerStyle),
				},computedObjs.map((item) => this.$_lms_renderNode(h,item)));
			}
		},
		$_lms_renderNode(h:CreateElement,computedObj:ComputedObj){
			return h('div',{
				staticClass:'lms-TreeSelect_option'+
					(computedObj.selected?' lms--selected':''),
				key:computedObj.key,
				on:{
					click:(e:Event) => {
						this.$_lms_onClick(computedObj,e);
					}
				}
			},this.$_lexmis_renderNodeContent(
				h,
				computedObj,
				this.$_lms_getContent({
					meta:computedObj
				}),
				QSpinner,
				Icon,
				Checkbox,
			));
		},
		$_lms_renderEmptyContent(h:CreateElement,slot:string,content:VNodeChildren){
			const scopedSlot=this.$scopedSlots[slot];
			return h('div',{
				staticClass:'lms-TreeSelect_fullContent',
			},
			scopedSlot
				?scopedSlot(undefined)
				:content
			);
		},
		$_lms_getContent(scope:NodeScope):VNodeChildren{
			const cell=this.$scopedSlots.content;
			const value=this.getCellValue(scope);
			if(cell){
				return cell({
					value,
					...scope,
				});
			}else{
				return value;
			}
		},
		getCellValue(scope:NodeScope):string{
			const {meta:{rawObj}}=scope;
			let value:string;
			if(typeof this.optionLabel==='string'){
				value=rawObj[this.optionLabel];
			}else{
				value=this.optionLabel(scope);
			}
			if(this.format){
				value=this.format(value);
			}
			return value+'';
		},
		$_lms_onClick(meta:ComputedObj,e:Event){
			if(meta.selectable){
				const target:RawObj=meta.rawObj;
				if(this.selected!==undefined){
					this.$emit('update:selected',target);
				}else{
					this.innerSelected=target;
				}
				if(this.emitOption){
					this.$emit('input',target);
				}else{
					this.$emit('input',target[this.optionValue]);
				}
				// @ts-ignore
				this.$refs.Tippy.hide();
			}
		},
	},
	render(h):VNode{
		return h(Tippy,{
			ref:'Tippy',
			staticClass:'lms-TreeSelect_Tippy',
			props:this.tippyOptions,
			on:{
				show:this.$_lms_show,
				hide:this.$_lms_hide,
				resize:(size:SizeObj) => {
					this.realWidth=size.width;
				}
			},
			scopedSlots:{
				content:() => (this.treeInfo&&this.treeInfo.computedObjs.length>0)
					?this.$_lms_renderNodes(h,this.treeInfo.computedObjs)
					:this.$_lms_renderEmptyContent(h,'no-data',this.noDataLabel),
				default:() => h('div',{
					staticClass:this.classes,
					attrs:{
						tabindex:this.disable?-1:0,
					},
					on:{
						keydown:(e:KeyboardEvent) => {
							if(e.key==='Enter'){
								if(this.focus){
									// @ts-ignore
									this.$refs.Tippy.hide();
								}else{
									// @ts-ignore
									this.$refs.Tippy.show();
								}
							}
						},
					}
				},[
					h('div',{
						staticClass:'lms-TreeSelect_content',
					},this.showValue),
					h(Icon,{
						staticClass:'lms-TreeSelect_arrow',
						props:{
							name:'play_arrow'
						}
					})
				])
			}
		});
	}
});
